﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Xml.Linq;
using UniversalEditor.Base;
using UniversalEditor.Base.FileSystem;
using UniversalEditor.Base.Mvvm;
using UniversalEditor.Base.StatusBarItems;
using System.Windows.Media;
using System.Windows.Documents;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Windows.Controls.Primitives;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using UniversalEditor.Base.Options;
using Brush = System.Windows.Media.Brush;
using Brushes = System.Windows.Media.Brushes;
using FontFamily = System.Windows.Media.FontFamily;
using Pen = System.Windows.Media.Pen;
using Point = System.Windows.Point;
using System.Collections;
using System.Reflection;
using System.Windows.Data;
using System.Collections.ObjectModel;


namespace UniversalEditor.Csv
{
    public class CsvEditor : EditorBase
    {
        private readonly Lazy<TableContainer> gridMain;
        private readonly SimpleCommand copyCommand;
        private readonly SimpleCommand cutCommand;
        private readonly SimpleCommand pasteCommand;
        private DataTable gridMainTable;
        private char spliter = ';';
        private char separatRow = '\n';
        private char separatColumn = '\t';
        private List<int> indexRows = new List<int>();
        public bool IsPaste { get; set; }
        private bool IsPasteEnable() { return IsPaste; }

        public CsvEditor(IEditorOwner owner, FileWrapper file)
            : base(owner, file)
        {
            IsPaste = false;
            gridMain = new Lazy<TableContainer>(CreateControl);
            pasteCommand = new SimpleCommand(PasteData, IsPasteEnable);
            copyCommand = new SimpleCommand(CopyData);
            cutCommand = new SimpleCommand(CutData);
            EditorControl.MouseRightButtonUp += EditorControl_MouseRightButtonUp;
            EditorControl.PreviewKeyDown += EditorControl_PreviewKeyDown;
            ObservableCollection<DataRowView> collectRows = GetData();
            gridMain.Value.FieldsProperty = collectRows;
        }

        private TableContainer CreateControl()
        {
            gridMainTable = new DataTable();
            gridMainTable.RowChanged += OnRowChanged;
            gridMainTable.RowDeleted += OnRowDeleted;
            gridMainTable.TableNewRow += OnTableNewRow;
            TableContainer ctrl = new TableContainer();
            return ctrl;
        }

        public override FrameworkElement EditorControl
        {
            get { return gridMain.Value; }
        }

        private void CutData()
        {
            if (gridMain.Value.i_CsvDG.SelectedCells.Count > 0)
            {
                string Data = SelectedData();
                int cuttingRow = indexRows[0];
                int cuttingColumn = gridMain.Value.i_CsvDG.SelectedCells[0].Column.DisplayIndex;
                SetClipboardData(0, Data, cuttingColumn, cuttingRow);
                IsPaste = true;
            }
        }

        private void CopyData()
        {
            if (gridMain.Value.i_CsvDG.SelectedCells.Count > 0)
            {
                string Data = SelectedData();
                SetClipboardData(1, Data, -1, -1);
                IsPaste = true;
            }
        }

        private void PasteData()
        {
            if (gridMain.Value.i_CsvDG.SelectedCells.Count > 0)
            {
                ClipboardData Data = (Clipboard.GetData("Command") as ClipboardData);
                if (Data != null)
                {
                    DataGridCell selectedCells = DataGridHelper.GetCell(gridMain.Value.i_CsvDG.SelectedCells[0]);
                    DataGridCellInfo selectedColumn = ((DataGridCellInfo)gridMain.Value.i_CsvDG.SelectedCells[0]);
                    int _firstRow = Convert.ToInt32(GetRowIndex(selectedCells));
                    string[] split = Data.dataClipboard.Replace("\r", separatColumn.ToString()).Split(new Char[] { separatRow });
                    int lastrow = _firstRow + split.Count();
                    while (gridMain.Value.FieldsProperty.Count < lastrow + 1) { createNewRow(); }
                    for (int k = _firstRow; k < lastrow; k++)
                    {
                        int startColumn = selectedColumn.Column.DisplayIndex;
                        startColumn = PasteRow(_firstRow, split, k, startColumn);
                    }
                    if (Data.typeClipboard == 0)
                    {
                        ClearCells(Data.dataClipboard, Data.cuttingRow, Data.cuttingColumn);
                        SetClipboardData(1, Data.dataClipboard, -1, -1);
                    }
                }
            }
        }

        private string SelectedData()
        {
            IList<DataGridCellInfo> selectedCells = ((DataGrid)gridMain.Value.FindName("i_CsvDG")).SelectedCells;
            string copyCells = string.Empty; string separatorRows = string.Empty; indexRows.Clear();
            bool isfirstStep = true;
            foreach (DataGridCellInfo di in selectedCells)
            {
                DataRowView selectedRow = (DataRowView)di.Item;
                var rowsCell = (di.Column as DataGridColumn).GetCellContent(di.Item);
                indexRows.Add(GetRowIndex((DataGridCell)rowsCell.Parent));
                int index = indexRows.Count - 1;
                if (!isfirstStep)
                {
                    separatorRows = ((indexRows[index] != indexRows[index - 1]) ? separatRow.ToString() : string.Empty);
                }
                isfirstStep = false;
                copyCells += separatorRows + selectedRow.Row[di.Column.DisplayIndex].ToString() + separatColumn.ToString();
            }
            return copyCells;
        }

        private int PasteRow(int _firstRow, string[] split, int k, int startColumn)
        {
            DataRowView currentRow = gridMain.Value.FieldsProperty[k];
            object[] curRowArray = currentRow.Row.ItemArray;
            string[] copyRow = (split[k - _firstRow]).Split('\t');
            for (int j = 0; j < (copyRow.Length - 1); j++)
            {
                curRowArray[startColumn] = copyRow[j]; startColumn++;
            }
            currentRow.Row.ItemArray = curRowArray;
            gridMain.Value.FieldsProperty[k] = currentRow;
            return startColumn;
        }

        private void SetClipboardData(int Type, string Data, int CutColumn, int CutRow)
        {
            ClipboardData _clipboardData = new ClipboardData();
            _clipboardData.typeClipboard = Type; _clipboardData.dataClipboard = Data;
            _clipboardData.cuttingColumn = CutColumn; _clipboardData.cuttingRow = CutRow;
            Clipboard.SetData("Command", _clipboardData);
        }

        private void ClearCells(string Data, int startRow, int _startColumn)
        {
            string[] line = Data.Split(new Char[] { separatRow });
            int lastrow = startRow + line.Count();
            for (int i = startRow; i < lastrow; i++)
            {
                int startColumn = _startColumn;
                DataRowView currentRow = gridMain.Value.FieldsProperty[i];
                object[] curRowArray = currentRow.Row.ItemArray;
                string[] copyRow = (line[i - startRow]).Split('\t');
                for (int j = 0; j < (copyRow.Length - 1); j++)
                {
                    curRowArray[startColumn] = string.Empty; startColumn++;
                }
                currentRow.Row.ItemArray = curRowArray;
                gridMain.Value.FieldsProperty[i] = currentRow;
            }
        }

        private void createNewRow()
        {
            DataRowView firstRow = gridMain.Value.FieldsProperty.First() as DataRowView;
            string[] currentRowArray = new string[gridMain.Value.FieldsProperty.First().Row.ItemArray.Length];
            for (int j = 0; j < firstRow.Row.ItemArray.Length; j++) { currentRowArray[j] = string.Empty; }
            DataTable dt = gridMain.Value.FieldsProperty.Last().Row.Table;
            dt.Rows.Add(currentRowArray);
            gridMain.Value.FieldsProperty.Add(dt.AsDataView()[dt.AsDataView().Count - 1]);
        }

        void EditorControl_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            EditorControl.ContextMenu = new ContextMenu();
            EditorControl.ContextMenu.Items.Add(new MenuItem { Header = "Copy", Command = copyCommand });
            EditorControl.ContextMenu.Items.Add(new MenuItem { Header = "Paste", Command = pasteCommand });
            EditorControl.ContextMenu.Items.Add(new MenuItem { Header = "Cut", Command = cutCommand });
        }

        void EditorControl_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            bool hasCtrl = (e.KeyboardDevice.Modifiers & ModifierKeys.Control) > 0;
            if (e.Key == Key.V && hasCtrl) { PasteData(); e.Handled = true; }
            if (e.Key == Key.C && hasCtrl) { CopyData(); e.Handled = true; }
        }

        private void UpdateMemoryFileContent()
        {
            _file.ClearLines();
            StringBuilder builder = new StringBuilder();
            int rowIndex = 0;
            foreach (DataRowView row in gridMain.Value.FieldsProperty)
            {
                builder.Clear();
                if (row.Row.ItemArray.Length > 0)
                    builder.Append(row.Row.ItemArray[0]);
                for (int index = 1; index < row.Row.ItemArray.Length; index++)
                {
                    builder.Append(spliter);
                    builder.Append(row.Row.ItemArray[index]);
                }
                _file.SetLine(rowIndex, builder.ToString().TrimEnd(spliter));
                rowIndex++;
            }
        }

        public ObservableCollection<DataRowView> GetData()
        {
            DataView dv = GetDataDV();
            for (int index = 0; index < dv.Table.Columns.Count; index++)
            {
                DataGridTextColumn Column = new DataGridTextColumn();
                Column.Header = dv.Table.Columns[index].ToString();
                Column.Binding = new Binding(dv.Table.Columns[index].ToString());
                gridMain.Value.i_CsvDG.Columns.Add(Column);
            }
            foreach (DataRowView dr in dv) gridMain.Value.FieldsProperty.Add(dr);
            return gridMain.Value.FieldsProperty;
        }

        private DataView GetDataDV()
        {
            try
            {
                gridMainTable.BeginLoadData(); gridMainTable.Clear(); gridMainTable.Columns.Clear();

                for (int index = 0; index < 26; index++)
                    gridMainTable.Columns.Add(GetHeaderFor(index));

                if (!string.IsNullOrEmpty(_file.FileSource))
                {
                    using (TextReader Reader = new StreamReader(File.OpenRead(_file.FileSource)))
                    {
                        bool isFirstLine = true;
                        string line;
                        string[] data = null;
                        while ((line = Reader.ReadLine()) != null)
                        {
                            if (isFirstLine)
                            {
                                isFirstLine = false;
                                spliter = line.Contains(";") ? ';' : line.Contains(",") ? ',' : ';';
                            }
                            data = line.Split(spliter);
                            while (data.Length > gridMainTable.Columns.Count)
                                gridMainTable.Columns.Add(GetHeaderFor(gridMainTable.Columns.Count));
                            gridMainTable.Rows.Add(data);
                        }
                    }
                }
                while (gridMainTable.Rows.Count <= 26)
                {
                    string[] datacol = null;
                    datacol = new string[gridMainTable.Columns.Count];
                    for (int j = 0; j < datacol.Length; j++) { datacol[j] = string.Empty; }
                    gridMainTable.Rows.Add(datacol);
                }
                return gridMainTable.AsDataView();
            }
            finally { gridMainTable.EndLoadData(); }
        }

        private string GetHeaderFor(int count)
        {
            StringBuilder Header = new StringBuilder();
            while (count >= 0)
            {
                if (count == 0) { Header.Insert(0, "A"); break; }
                int x = count % 26;
                char ch = Convert.ToChar(x + Convert.ToInt32('A'));
                count = (count - x) / 26 - 1;
                Header.Insert(0, ch);
            }
            return Header.ToString();
        }

        public static class DataGridHelper
        {
            public static DataGridCell GetCell(DataGridCellInfo dataGridCellInfo)
            {
                if (!dataGridCellInfo.IsValid) { return null; }
                var cellContent = dataGridCellInfo.Column.GetCellContent(dataGridCellInfo.Item);
                if (cellContent != null) { return (DataGridCell)cellContent.Parent; }
                else { return null; }
            }
        }

        public int GetRowIndex(DataGridCell dataGridCell)
        {
            PropertyInfo rowDataItemProperty = dataGridCell.GetType().GetProperty("RowDataItem", BindingFlags.Instance | BindingFlags.NonPublic);
            DataGrid dataGrid = GetDataGridFromChild(dataGridCell);
            return dataGrid.Items.IndexOf(rowDataItemProperty.GetValue(dataGridCell, null));
        }

        public static DataGrid GetDataGridFromChild(DependencyObject dataGridPart)
        {
            if (VisualTreeHelper.GetParent(dataGridPart) == null)
            {
                throw new NullReferenceException("Control is null.");
            }
            if (VisualTreeHelper.GetParent(dataGridPart) is DataGrid)
            {
                return (DataGrid)VisualTreeHelper.GetParent(dataGridPart);
            }
            else { return GetDataGridFromChild(VisualTreeHelper.GetParent(dataGridPart)); }
        }

        public override ICommand CutCommand { get { return copyCommand; } }

        public override ICommand PasteCommand { get { return pasteCommand; } }

        private void OnTableNewRow(object sender, DataTableNewRowEventArgs e) { }

        private void OnRowDeleted(object sender, DataRowChangeEventArgs e) { }

        private void OnRowChanged(object sender, DataRowChangeEventArgs e) { }

        public override void Dispose()
        {
            if (gridMain.IsValueCreated)
            {
                gridMain.Value.DataContext = null;
                gridMainTable.RowChanged -= OnRowChanged;
                gridMainTable.RowDeleted -= OnRowDeleted;
                gridMainTable.TableNewRow -= OnTableNewRow;
            }
            base.Dispose();
        }

        public override void SaveFile()
        {
            SaveFileAs(_file.FilePath.Replace(".csv", ""));
        }

        public override void SaveFileAs(string filePath)
        {
            UpdateMemoryFileContent();
            _file.SaveAs(filePath.Replace(".csv", "") + ".csv");
        }

        public override void SaveCopyAs(string filePath)
        {
            UpdateMemoryFileContent();
            _file.SaveCopyAs(filePath.Replace(".csv", "") + ".csv");
        }
    }

    [Serializable]
    public class ClipboardData
    {
        public int typeClipboard { get; set; }
        public string dataClipboard { get; set; }
        public int cuttingRow { get; set; }
        public int cuttingColumn { get; set; }
    }
}
